﻿using AJWPFAdmin.Core.Components.AJTable.ViewModels;
using AJWPFAdmin.Core.Crypto.AES;
using AJWPFAdmin.Core.Crypto.RSA;
using AJWPFAdmin.Core.Crypto.SMCrypto;
using AJWPFAdmin.Core.Enums;
using AJWPFAdmin.Core.ExtensionMethods;
using AJWPFAdmin.Core.GlobalEvents;
using AJWPFAdmin.Core.HardwareSDKS.Models;
using AJWPFAdmin.Core.Logger;
using AJWPFAdmin.Core.Models;
using AJWPFAdmin.Core.Models.EF.Tables;
using AJWPFAdmin.Core.Utils;
using AJWPFAdmin.Services.EF;
using AJWPFAdmin.Services.EF.TableExts;
using AJWPFAdmin.Services.Models;
using Azure;
using com.github.xiangyuecn.rsacsharp;
using DnsClient.Protocol;
using Masuit.Tools.Reflection;
using Masuit.Tools.Security;
using MaterialDesignThemes.Wpf;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Prism.Ioc;
using Quartz.Logging;
using Quartz.Util;
using Quartz.Xml.JobSchedulingData20;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Reflection.Emit;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
using System.Windows.Interop;
using Yitter.IdGenerator;
using static AJWPFAdmin.Services.Jobs.AppointAPIUploadFailedRecordJob;

namespace AJWPFAdmin.Services.AppointAPI
{
    /// <summary>
    /// 四川德阳台账接口实现 --阿吉 2025年7月24日17点28分
    /// </summary>
    public class DeyangAppointAPI : AppointAPIBase
    {
        private APIConfig _apiConfig;

        private static readonly object _tokenLocker = new object();

        private System.Timers.Timer _netAndTimeSyncTimer;
        private System.Timers.Timer _alarmInfoSyncTimer;
        private System.Timers.Timer _blackedListSyncTimer;

        private List<string> _unSignFields;

        private Dictionary<string, string> _truckTypesMap;
        private Dictionary<string, string> _plateColorsMap;

        public DeyangAppointAPI(ApplicationType appType,
            SystemConfigDictionary cfg,
            IContainerProvider svcProvider) : base(appType, cfg, svcProvider)
        {
            _unSignFields = new List<string>
            {
                "token",
                "detailedList","drivingLicense","machinePlatePhoto","enginePlatePhoto","greenLabelPhoto",
                "frontPhoto","bodyPhoto",
                "videoUrl","localVideoUrl",
                "rtspAdd"
            };

            _truckTypesMap = new Dictionary<string, string>
            {
                {"1","微型车" },
                {"2","小型车" },
                {"3","紧凑型车" },
                {"4","中型车" },
                {"5","中大型车" },
            };
            _plateColorsMap = new Dictionary<string, string>
            {
                {"0","蓝色" },
                {"1","黄色" },
                {"2","白色" },
                {"3","黑色" },
                {"4","新能源" },
                {"5","其他" },
            };
        }

        public new void Dispose()
        {
            _netAndTimeSyncTimer?.Stop();
            _alarmInfoSyncTimer?.Stop();
            _blackedListSyncTimer?.Stop();
        }

        public override Task ConvertToCustomConfig(bool isReal)
        {
            _apiConfig = CommonUtil.TryGetJSONObject<APIConfig>(Config.StringValue) ?? new APIConfig();
            _customAPIConfig = _apiConfig;
            AutoUploadDataVerification = _apiConfig.AutoUploadDataVerification;

            if (isReal)
            {
                if (_appType == ApplicationType.岗亭端)
                {
                    _netAndTimeSyncTimer = new System.Timers.Timer
                    {
                        AutoReset = true,
                        Interval = TimeSpan.FromMinutes(10).TotalMilliseconds
                    };
                    _netAndTimeSyncTimer.Elapsed += NetAndTimeSyncTimerHandle;
                    _netAndTimeSyncTimer.Start();
                }
                else
                {
                    _alarmInfoSyncTimer = new System.Timers.Timer
                    {
                        AutoReset = true,
                        Interval = TimeSpan.FromMinutes(30).TotalMilliseconds
                    };
                    _alarmInfoSyncTimer.Elapsed += AlarmInfoSyncTimerHandle;
                    _alarmInfoSyncTimer.Start();

                    _blackedListSyncTimer = new System.Timers.Timer
                    {
                        AutoReset = true,
                        Interval = TimeSpan.FromMinutes(10).TotalMilliseconds
                    };
                    _blackedListSyncTimer.Elapsed += BlackedListSyncTimerHandle;
                    _blackedListSyncTimer.Start();
                }
            }
            else
            {
                _ = GetSchemas();
            }

            return Task.CompletedTask;
        }

        public override async Task<ProcessResult> GetNotAuditCarListAsync(NotAuditCarListParams @params)
        {
            var query = new JObject
            {
                {"comCode",_apiConfig.ComCode },
            };
            if (@params.Start.HasValue && @params.Start != DateTime.MinValue)
            {
                query.Add("beginTime", @params.Start.GetValueOrDefault().ToCommonDateString());
            }
            if (@params.End.HasValue && @params.End != DateTime.MinValue)
            {
                query.Add("endTime", @params.End.GetValueOrDefault().ToCommonDateString());
            }
            var result = await PostAsync("/enterpriseApi/downLoadIncorrectCar", query);

            if (!result.Success)
            {
                return result;
            }

            var res = result.Data as APIResponseInner;

            var resArray = CommonUtil.TryGetJSONObject<JArray>(res.msg);

            var rows = new List<NotAuditCarInfo>();

            if ((resArray?.Count).GetValueOrDefault() > 0)
            {
                foreach (var item in resArray)
                {
                    rows.Add(new NotAuditCarInfo
                    {
                        PlateNumber = item["plateNumber"].ToString(),
                        CarType = item["carType"].ToString(),
                        Status = item["status"].ToObject<int>() == 3 ? "不通过" : "审核中",
                        Remark = item["remark"].ToString()
                    });
                }
            }

            var paged = new AJTableFetchResult
            {
                HasNext = false,
                Total = rows.Count,
                Rows = rows
            };

            result.SetSuccess(paged);

            return result;
        }

        public override async Task<ProcessResult> DownLoadEmergencyFileAsync()
        {
            var result = await PostAsync("/enterpriseApi/downLoadEmergencyFile", new JObject
            {
                {"comCode",_apiConfig.ComCode },
            });

            if (!result.Success)
            {
                return result;
            }

            var res = result.Data as APIResponseInner;

            var resArray = CommonUtil.TryGetJSONObject<JArray>(res.msg);

            var rows = new List<EmergencyFileItem>();

            if ((resArray?.Count).GetValueOrDefault() > 0)
            {
                foreach (var item in resArray)
                {
                    rows.Add(new EmergencyFileItem
                    {
                        Name = item["name"].ToString(),
                        Url = item["url"].ToString()
                    });
                }
            }

            var paged = new AJTableFetchResult
            {
                HasNext = false,
                Total = rows.Count,
                Rows = rows
            };

            result.SetSuccess(paged);

            return result;
        }

        public override async Task<ProcessResult> QueryBlackListAsync(CarInfoParams @params)
        {
            var carNo = @params.CarInfo.CarNo;

            using var db = DbService.GetDbContext(_configService);

            var black = await db.BlacklistedCars.AnyAsync(p => p.CarNo == carNo);

            var result = new ProcessResult();

            result.SetSuccess(black);

            return result;
        }

        public override async Task<ProcessResult> GetAlarmInfosAsync()
        {
            var result = await PostAsync("/enterpriseApi/getAlarmInfo", new JObject
            {
                {"comCode",_apiConfig.ComCode }
            });

            if (!result.Success)
            {
                return result;
            }

            var res = result.Data as APIResponseInner;

            var resArray = CommonUtil.TryGetJSONObject<JArray>(res.msg);

            if (resArray == null || resArray.Count == 0)
            {
                return result;
            }

            using var db = DbService.GetDbContext(_configService);

            var records = new List<AlarmInfoRecord>();

            foreach (var item in resArray)
            {
                records.Add(new AlarmInfoRecord
                {
                    Id = YitIdHelper.NextId(),
                    Type = item["type"].ToString(),
                    DeviceCode = item["vedioDeviceId"]?.ToString(),
                    RecordRel = item["vehiclePassRecordId"].ToString(),
                    WarningType = item["warningType"].ToString(),
                    Params = item["alarmParams"].ToString(),
                    CreateDate = DateTime.Now
                });
            }

            db.AlarmInfoRecords.AddRange(records);

            await db.SaveChangesAsync();

            return result;
        }

        public override async Task<ProcessResult> QueryManageMeasureListAsync(QueryManageMeasureParams @params)
        {
            var result = await PostAsync("/enterpriseApi/getWarningInfo", new JObject
            {
                {"comCode",_apiConfig.ComCode },
            });

            if (!result.Success)
            {
                return result;
            }

            var res = result.Data as APIResponseInner;

            var resObj = CommonUtil.TryGetJSONObject<JObject>(res.msg);

            var rows = new List<ManageMeasureListItem>();

            if (resObj != null)
            {
                var levelStr = resObj["warningLevel"]?.ToString() ?? ((int)ManageMeasureWarnLevel.自定义管控).ToString();

                _ = Enum.TryParse<ManageMeasureWarnLevel>(levelStr, out var level);

                var carTypes = resObj["carTypeWarningList"].ToObject<string[]>() ?? Array.Empty<string>();
                var colors = resObj["plateColorWarningList"].ToObject<string[]>() ?? Array.Empty<string>();

                var carTypeDesc = carTypes.Length == 0 ? "暂无" : string.Join(",", carTypes.Select(p => _truckTypesMap[p]));

                var colorDesc = colors.Length == 0 ? "暂无" : string.Join(",", colors.Select(p => _plateColorsMap[p]));

                rows.Add(new ManageMeasureListItem
                {
                    WarnStartTime = resObj["startTime"]?.ToString(),
                    WarnEndTime = resObj["endTime"]?.ToString(),
                    WarnLevel = level,
                    //ResponseLevel = resLevel,carTypeWarningList
                    ControlMeasure = $"管控车辆类型:{carTypeDesc};管控车牌颜色:{colorDesc}"
                });
            }

            var paged = new AJTableFetchResult
            {
                HasNext = false,
                Total = rows.Count,
                Rows = rows
            };

            result.SetSuccess(paged);

            return result;
        }

        public override Task<ProcessResult> UploadDataVerificationAsync(DataVerificationParams @params)
        {
            return PostAsync("/enterpriseApi/companyVehicleRecordSumUpload", new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"recordNumOfDay",@params.VechicleNum.ToString() },
                {"truckRecordNumOfDay",@params.InOutFactoryVechicleNum.ToString()},
                {"otherRecordNumOfDay",(@params.InVechicleNum + @params.NrVehicleNum).ToString() },
                {"beginTime",@params.RecordDate.ToCommonDateString() },
                {"endTime",@params.RecordEndDate.ToCommonDateString() },
            });
        }

        public override async Task<ProcessResult> CaptureVideoAsync(CaptureVideoParams @params)
        {
            var result = new ProcessResult();

            if (!_apiConfig.EnableVideoCapture)
            {
                result.SetSuccess();
                return result;
            }

            if (_apiConfig.VideoDuration <= 0)
            {
                var logs = "尚未配置录制时长\r\n";
                OnAPILogsRaised(@params.Record.Direction, logs);
                result.SetError(logs);
                return result;
            }
            if (@params.HIKDevice is not HIKVisionDevice hikDevice)
            {
                var logs = "设备异常或不支持录制视频\r\n";
                OnAPILogsRaised(@params.Record.Direction, logs);
                result.SetError(logs);
                return result;
            }

            var saveDir = Path.Combine(_carIdentificationConfig.ImageSavePath, @params.Record.CarNo);

            OnAPILogsRaised(@params.Record.Direction,
                $"开始录制视频,时长:{_apiConfig.VideoDuration} 秒,请耐心等待\r\n");

            result = await hikDevice.StartVideoCapture(new DeviceVideoCaptureCmdParameter
            {
                VideoDuration = _apiConfig.VideoDuration,
                StoreDirectory = saveDir,
                CarPlateNumber = @params.Record.CarNo
            });

            var videoFileName = result.Data as string;

            if (!result.Success || string.IsNullOrWhiteSpace(videoFileName))
            {
                var logs = $"录制视频失败:{result.Message}\r\n";
                OnAPILogsRaised(@params.Record.Direction, logs);
                result.SetError(logs);
                return result;
            }

            OnAPILogsRaised(@params.Record.Direction, $"正在上传视频至远程台账接口..\r\n");

            var uploadRet = await UploadVideoAsync(@params.Record, videoFileName);

            if (!uploadRet.Success)
            {
                result.SetError($"上传失败:{uploadRet.Message}");
            }
            else
            {
                videoFileName = uploadRet.Data as string;
                result.SetSuccess(videoFileName);
            }

            return result;
        }

        public override async Task<ProcessResult> UploadShippingRecordAsync(ShippingRecordParams @params)
        {
            var result = new ProcessResult();
            var record = @params.Record;

            var response = new UploadShippingRecordResponse();

            var carTypeEnum = (CarTypeEnum)((int)record.TypeId);
            if (carTypeEnum > CarTypeEnum.非道路移动机械)
            {
                result = new ProcessResult();
                result.SetSuccess();
                result.Attach = response;
                return result;
            }
            var categoryType = carTypeEnum == CarTypeEnum.进出厂车 ? 1
                : carTypeEnum == CarTypeEnum.厂内运输车 ? 2
                : carTypeEnum == CarTypeEnum.非道路移动机械 ? 3 : 4;

            var (carHead, _, carBody) = @params.Record.GetWatchPositionImageFile(@params.Record.Direction);

            var frontPhotoLocalUrl = _carIdentificationConfig.CompleteFileUrl(record.CarNo, carHead);

            var frontPhoto = await GetServerFileAsync(frontPhotoLocalUrl, errLogPrefix: $"{record.CarNo}上传车头图片");

            var bodyPhotoLocalUrl = _carIdentificationConfig.CompleteFileUrl(record.CarNo, carBody);

            var bodyPhoto = await GetServerFileAsync(bodyPhotoLocalUrl, errLogPrefix: $"{record.CarNo}上传车身图片");

            result = await PostAsync("/enterpriseApi/vehicleRecord", new JObject
            {
                {"comCode",_apiConfig.ComCode },

                {"brakeCode",record.Direction == PassagewayDirection.进
                ? record.PassagewayCode : record.OutPassagewayCode },

                {"recordId",record.Id.ToString() },
                {"plateNumber",record.CarNo },
                {nameof(categoryType),categoryType },
                {"plateColor", ((byte)record.PlateColor).ToString() },
                {"passTime",record.Direction == PassagewayDirection.进 ?  record.ShipStartDate.GetValueOrDefault().ToCommonDateString()
                : record.ShipEndDate.GetValueOrDefault().ToCommonDateString()},
                {"throughType",record.Direction == PassagewayDirection.进 ? "1" : "2" },
                {"triggerType",record.AutoPass ? "1" : "2"},
                {nameof(frontPhoto),frontPhoto },
                {nameof(frontPhotoLocalUrl),CombineLocalUrl(frontPhotoLocalUrl,record.CarNo)},
                {nameof(bodyPhoto),bodyPhoto },
                {nameof(bodyPhotoLocalUrl),CombineLocalUrl(bodyPhotoLocalUrl,record.CarNo) },
            });

            if (result.Success)
            {
                // 过车记录上传成功,记录号加1
                response.RecordNum++;
            }

            result.Attach = response;

            // 如果是进厂 则不先上传进厂的物资信息,等出厂的时候补传
            if (@params.Record.Direction == PassagewayDirection.进)
            {
                return result;
            }

            #region 上传入厂物资

            result = await VehicleRecordCompletionAsync(record,
                PassagewayDirection.进);

            if (!result.Success)
            {
                result.SetError($"{@params.Record.CardNo} {KeyDescription}接口 [入厂] 物资信息上传失败:{result.Message}");
            }
            else
            {
                // 货物记录上传成功,物资记录号加1
                response.GoodsNum++;
            }

            #endregion

            #region 上传出厂物资

            result = await VehicleRecordCompletionAsync(record,
                PassagewayDirection.出);

            if (!result.Success)
            {
                result.SetError($"{@params.Record.CardNo} {KeyDescription}接口 [出厂] 物资信息上传失败:{result.Message}");
            }
            else
            {
                // 货物记录上传成功,物资记录号加1
                response.GoodsNum++;
            }

            #endregion

            result.Attach = response;

            return result;
        }

        private string CombineLocalUrl(string localFile, string carNo)
        {
            return $"{_apiConfig.PrivateNetworkAddress}/{Path.Combine(_apiConfig.FileFolder, carNo, Path.GetFileName(localFile))}";
        }

        private Task<ProcessResult> VehicleRecordCompletionAsync(ShippingRecord record,
            PassagewayDirection direction)
        {
            return PostAsync("/enterpriseApi/vehicleRecordCompletion", new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"brakeCode",  direction == PassagewayDirection.进 ? record.PassagewayCode : record.OutPassagewayCode},
                {"recordId", record.Id.ToString() },
                {"plateNumber",record.CarNo },
                {"goodsName", direction == PassagewayDirection.进 ? record.InGoodsName : record.OutGoodsName },
                {"goodsWeight", direction == PassagewayDirection.进
                ? record.InGoodsVolume.ToString() : record.InGoodsVolume.ToString() }
            });
        }

        private async void AlarmInfoSyncTimerHandle(object sender, System.Timers.ElapsedEventArgs e)
        {
            await GetAlarmInfosAsync();
        }

        private async void BlackedListSyncTimerHandle(object sender, System.Timers.ElapsedEventArgs e)
        {
            _logger.Debug($"开始同步黑名单数据");
            var result = await PostAsync("/enterpriseApi/getBlackListInfo", new JObject
            {
                {"comCode",_apiConfig.ComCode },
            });

            if (!result.Success)
            {
                _logger.Debug($"同步黑名单列表失败:{result.Message}");
                return;
            }

            var res = result.Data as APIResponseInner;

            var resArray = CommonUtil.TryGetJSONObject<JArray>(res.msg);

            if (resArray == null || resArray.Count == 0)
            {
                _logger.Debug($"没有任何黑名单数据");
                return;
            }

            var added = new List<BlacklistedCar>();
            var updated = 0;

            using var db = DbService.GetDbContext(_configService);

            var dbList = await db.BlacklistedCars.ToListAsync();

            var now = DateTime.Now;

            foreach (var item in resArray)
            {
                var carNo = item["plateNo"].ToString();

                var dbItem = dbList.FirstOrDefault(p => p.CarNo == carNo);

                if (dbItem == null)
                {
                    added.Add(new BlacklistedCar
                    {
                        Id = YitIdHelper.NextId(),
                        CarNo = carNo,
                        PlateColor = item["plateColor"].ToString(),
                        VIN = item["vin"].ToString(),
                        FuelTypeName = item["flueType"].ToString(),
                        EmissionStandard = item["emissionStandard"].ToString(),
                        Reason = item["remark"].ToString(),
                        CreateDate = now
                    });
                }
                else
                {
                    dbItem.PlateColor = item["plateColor"].ToString();
                    dbItem.VIN = item["vin"].ToString();
                    dbItem.FuelTypeName = item["flueType"].ToString();
                    dbItem.EmissionStandard = item["emissionStandard"].ToString();
                    dbItem.Reason = item["remark"].ToString();
                    dbItem.UpdateDate = now;
                    updated++;
                    db.Entry(dbItem).State = EntityState.Modified;
                }
            }

            if (added.Count > 0)
            {
                db.BlacklistedCars.AddRange(added);
            }

            if (updated > 0 || added.Count > 0)
            {
                await db.SaveChangesAsync();
            }
            _logger.Debug($"同步黑名单数据完成: 新增 {added.Count};更新:{updated}");
        }

        private async void NetAndTimeSyncTimerHandle(object sender, System.Timers.ElapsedEventArgs e)
        {
            if ((AppointAPIUploadFailedRecordQuarztJob.Devices?.Count).GetValueOrDefault() == 0)
            {
                return;
            }

            var watchhouseIds = AppointAPIUploadFailedRecordQuarztJob.Devices
                .Select(p => p.WatchhouseId).Distinct().ToArray();

            var watchhouseSource = await db.Watchhouses
                .Include(p => p.Passageways).AsNoTracking().ToListAsync();

            var passagewayIds = watchhouseSource
                .SelectMany(p => p.Passageways).Select(p => p.Id).ToArray();

            var dbDevices = db.Devices
                .Where(p => passagewayIds.Contains(p.PassagewayId) && p.Type < DeviceType.高频读头)
                .AsNoTracking().ToList();

            foreach (var watchhouse in watchhouseSource)
            {
                foreach (var passageway in watchhouse.Passageways)
                {
                    var devs = dbDevices
                        .Where(p => p.PassagewayId == passageway.Id).ToList();

                    foreach (var dev in devs)
                    {
                        await NetStatusAndTimeSynchronizationAsync(passageway, dev);
                    }
                }
            }
        }

        public override Task<ProcessResult> UploadEntGateInfoAsync(WatchhouseInfoParams @params)
        {
            return PostAsync("/enterpriseApi/gate", new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"name",@params.Watchhouse.Name },
                {"gateCode",@params.Watchhouse.Code },
                {"inOrOut",@params.Watchhouse.Direction.GetValueOrDefault() == PassagewayDirection.进 ? 1 : 2 },
            });
        }

        public override Task<ProcessResult> UploadPssagewayAsync(PssagewayParams @params)
        {
            return PostAsync("/enterpriseApi/brake", new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"gateCode",@params.Watchhouse.Code },
                {"brakeCode",@params.Passageway.Code },
                {"name",@params.Passageway.Name },
                {"inOrOut",@params.Passageway.Direction == PassagewayDirection.进 ? 1 : 2 },
                {"address",@params.Passageway.Address },
                {"contactPerson",@params.Passageway.CompanyPerson },
                {"contactNumber",@params.Passageway.CompanyPhone },
            });
        }

        public override async Task<ProcessResult> UploadCarInfoAsync(CarInfoParams @params)
        {
            var (action, body) = await ProcessCarInfoToUploadAsync(@params);
            if (string.IsNullOrEmpty(action))
            {
                return ProcessResult.DefaultSuccess;
            }

            var result = await PostAsync($"/enterpriseApi/{action}", body,
                needUILog: true,
                direction: @params.Passageway?.Direction);

            if (!result.Success)
            {
                return result;
            }

            var innerRes = result.Data as APIResponseInner;

            if (innerRes.result == "3")
            {
                result.SetError(innerRes.reason, innerRes);
            }
            return result;
        }

        private async Task<(string action, JObject body)> ProcessCarInfoToUploadAsync(CarInfoParams @params)
        {
            var action = string.Empty;
            JObject body = null;
            var carNo = @params.CarInfo.CarNo;

            var detailedListLocalUrl = _carIdentificationConfig.CompleteFileUrl(carNo, @params.CarInfo.GetFileOfPropertyName(nameof(Car.Attachments)));

            var drivingLicenseLocalUrl = _carIdentificationConfig.CompleteFileUrl(carNo, @params.CarInfo.GetFileOfPropertyName(nameof(Car.VehicleLicense)));

            var detailedList = string.Empty;
            var drivingLicense = string.Empty;

            if (@params.CarInfo.TypeId == (long)CarTypeEnum.厂内运输车
                || @params.CarInfo.TypeId == (long)CarTypeEnum.进出厂车)
            {
                detailedList = await GetServerFileAsync(detailedListLocalUrl, errLogPrefix: $"{carNo}上传随车清单");

                drivingLicense = await GetServerFileAsync(drivingLicenseLocalUrl, errLogPrefix: $"{carNo}上传行驶证");
            }

            if (@params.CarInfo.TypeId == (long)CarTypeEnum.厂内运输车)
            {
                action = @params.Operation == DataOperationType.新增 ? "factoryVehicle" : "factoryVehicleUpdate";
                body = new JObject
                {
                    {"comCode",_apiConfig.ComCode },
                    {"greenNumber",@params.CarInfo.EnvRegCode },
                    {"plateNumber",@params.CarInfo.CarNo },
                    {"regDate",@params.CarInfo.RegDate.GetValueOrDefault().ToString("yyyy-MM-dd")},
                    {"productDate",@params.CarInfo.ProductionDate.GetValueOrDefault().ToString("yyyy-MM-dd")},
                    {"vin",@params.CarInfo.VIN },
                    {"engineNo",@params.CarInfo.EngineNo },
                    {"fuelType",@params.CarInfo.FuelTypeCode },
                    {"emissionStandard",@params.CarInfo.EmissionStandard.GetDescription() },
                    {nameof(detailedList),detailedList },
                    {nameof(detailedListLocalUrl),CombineLocalUrl(detailedListLocalUrl,@params.CarInfo.CarNo) },
                    {nameof(drivingLicense),drivingLicense },
                    {nameof(drivingLicenseLocalUrl),CombineLocalUrl(drivingLicenseLocalUrl,@params.CarInfo.CarNo) },
                    {"originInfo",@params.CarInfo.Owner },
                };
            }
            else if (@params.CarInfo.TypeId == (long)CarTypeEnum.非道路移动机械)
            {
                var machinePlatePhotoLocalUrl = _carIdentificationConfig.CompleteFileUrl(carNo, @params.CarInfo.GetFileOfPropertyName(nameof(Car.Nameplate)));
                var machinePlatePhoto = await GetServerFileAsync(machinePlatePhotoLocalUrl, errLogPrefix: $"{carNo} 上传整车铭牌");

                var enginePlatePhotoLocalUrl = _carIdentificationConfig.CompleteFileUrl(carNo, @params.CarInfo.GetFileOfPropertyName(nameof(Car.EngineNameplate)));
                var enginePlatePhoto = await GetServerFileAsync(enginePlatePhotoLocalUrl, errLogPrefix: $"{carNo} 上传发动机铭牌");

                var greenLabelPhotoLocalUrl = _carIdentificationConfig.CompleteFileUrl(carNo, @params.CarInfo.GetFileOfPropertyName(nameof(Car.MachineEnvProtectionLabel)));
                var greenLabelPhoto = await GetServerFileAsync(greenLabelPhotoLocalUrl, errLogPrefix: $"{carNo} 上传环保标签");

                action = @params.Operation == DataOperationType.新增 ? "machineVehicle" : "machineVehicleUpdate";
                body = new JObject
                {
                    {"comCode",_apiConfig.ComCode },
                    {"greenNumber",@params.CarInfo.EnvRegCode },
                    {"machineType",MapMachineType(@params.CarInfo.MachineType.GetValueOrDefault()) },
                    {"pin",@params.CarInfo.PIN},
                    {"productDate",@params.CarInfo.ProductionDate.GetValueOrDefault().ToString("yyyy-MM-dd")},
                    {"fuelType",@params.CarInfo.FuelTypeCode },
                    {"emissionStandard",@params.CarInfo.EmissionStandard.GetDescription() },
                    {nameof(machinePlatePhoto),machinePlatePhoto },
                    {nameof(machinePlatePhotoLocalUrl),CombineLocalUrl(machinePlatePhotoLocalUrl,@params.CarInfo.CarNo) },
                    {nameof(enginePlatePhoto),enginePlatePhoto },
                    {nameof(enginePlatePhotoLocalUrl),CombineLocalUrl(enginePlatePhotoLocalUrl,@params.CarInfo.CarNo) },
                    {nameof(greenLabelPhoto),greenLabelPhoto},
                    {nameof(greenLabelPhotoLocalUrl),CombineLocalUrl(greenLabelPhotoLocalUrl,@params.CarInfo.CarNo) },
                    {"originInfo",@params.CarInfo.Owner },
                    {"machineStatus",@params.CarInfo.NetworkingStatus ? "1" : "2" },
                };
            }
            else if (@params.CarInfo.TypeId == (long)CarTypeEnum.进出厂车)
            {
                action = @params.Operation == DataOperationType.新增 ? "vehicle" : "vehicleUpdate";

                var typeStr = @params.CarInfo.VehicleType.ToString();
                var map = _truckTypesMap.FirstOrDefault(p => p.Value == typeStr);

                body = new JObject
                {
                    {"comCode",_apiConfig.ComCode },
                    {"carType",map.Key },
                    {"plateNumber",@params.CarInfo.CarNo },
                    {"plateColor", ((byte)@params.CarInfo.PlateColor).ToString() },
                    {"regDate",@params.CarInfo.RegDate.GetValueOrDefault().ToString("yyyy-MM-dd")},
                    {"vin",@params.CarInfo.VIN },
                    {"engineNo",@params.CarInfo.EngineNo },
                    {"fuelType",@params.CarInfo.FuelTypeCode },
                    {"emissionStandard",@params.CarInfo.EmissionStandard.GetDescription() },
                    {"useType",@params.CarInfo.UsageDescription },
                    {nameof(detailedList),detailedList },
                    {nameof(detailedListLocalUrl),CombineLocalUrl(detailedListLocalUrl,@params.CarInfo.CarNo) },
                    {nameof(drivingLicense),drivingLicense },
                    {nameof(drivingLicenseLocalUrl),CombineLocalUrl(drivingLicenseLocalUrl,@params.CarInfo.CarNo) },
                    {"originInfo",@params.CarInfo.TeamName },
                };
            }

            return (action, body);
        }

        private string MapMachineType(MachineType machineType)
        {
            var result = "5";
            switch (machineType)
            {
                case MachineType.装载机:
                    result = "1";
                    break;
                case MachineType.叉车:
                    result = "2";
                    break;
                case MachineType.泵车:
                    result = "3";
                    break;
                case MachineType.清扫车:
                    result = "4";
                    break;
                case MachineType.其他:
                    break;
                default:
                    break;
            }
            return result;
        }

        public override Task<ProcessResult> UploadDeviceInfoAsync(DeviceInfoParams @params)
        {
            return PostAsync("/enterpriseApi/vedioDevice", new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"code",@params.Device.Code },
                {"name",@params.Device.Name },
                {"brandType",@params.Device.Type == DeviceType.监控相机_海康 ? "1" : "2" },
                {"deviceType","2" },
                {"userName",@params.Device.LoginName },
                {"password",@params.Device.LoginPassword },
                {"ip",@params.Device.IP },
                {"port",@params.Device.Port },
                {"channel", "1" },
                {"rtspAdd" , @params.Device.VideoPlayRTSPUrl },
            });
        }

        private async Task<ProcessResult> UploadVideoAsync(ShippingRecord record, string file)
        {
            var result = new ProcessResult();
            var videoUrl = await GetServerFileAsync(file, 1, $"{record.CarNo} 上传过车视频");

            if (string.IsNullOrEmpty(videoUrl))
            {
                result.SetError("请查看后台日志");
                return result;
            }

            result = await PostAsync("/enterpriseApi/machineVehicleRecord", new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"brakeCode",  record.Direction == PassagewayDirection.进 ? record.PassagewayCode : record.OutPassagewayCode},
                {"recordId", record.Id.ToString() },
                {"plateNumber",record.CarNo },
                {"plateColor", ((byte)record.PlateColor).ToString() },
                {nameof(videoUrl),videoUrl },
                {"videoBeginTime", record.CreateDate.ToCommonDateString() },
                {"videoEndTime", record.CreateDate.AddSeconds(_apiConfig.VideoDuration).ToCommonDateString() },
                {"localVideoUrl", file }
            });

            if (!result.Success)
            {
                return result;
            }

            result.SetSuccess(videoUrl);

            return result;


        }

        private async Task<string> GetServerFileAsync(string file,
            int fileType = 0,
            string errLogPrefix = "")
        {
            var result = string.Empty;

            // 占位文件
            var name = $"{Path.GetFileNameWithoutExtension(file)}.ph";
            var dir = Directory.GetParent(file);
            var placeholderFile = Path.Combine(dir.FullName, name);

            if (File.Exists(placeholderFile))
            {
                try
                {
                    result = await File.ReadAllTextAsync(placeholderFile, Encoding.UTF8);
                    return result;
                }
                catch
                {
                }
            }

            var uploadRet = await UploadFileAsync(file, fileType);
            if (!uploadRet.Success)
            {
                _logger.Error($"{errLogPrefix} [{file}] 失败:{uploadRet.Message}");
            }
            else
            {
                result = uploadRet.Data as string;
                // 上传成功写入一个占位文件
                try
                {
                    await File.WriteAllTextAsync(placeholderFile, result, Encoding.UTF8);
                }
                catch
                {
                }
            }

            return result;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileType"> 0 图片 1视频</param>
        /// <returns></returns>
        private Task<ProcessResult> UploadFileAsync(string file, int fileType = 0)
        {
            return UploadAsync("/enterpriseApi/uploadFile", file, new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"fileType", fileType.ToString()}
            });
        }

        private Task<ProcessResult> NetStatusAndTimeSynchronizationAsync(
            Passageway passageway,
            Device device)
        {
            return PostAsync("/enterpriseApi/netStatusAndTimeSynchronization", new JObject
            {
                {"comCode",_apiConfig.ComCode },
                {"brakeCode",passageway.Code },
                {"brakeStatus",0 },
                {"carDeviceStatus",0 },
                {"videoDeviceCode",device.Code },
                {"videoDeviceStatus",0 },
            });
        }

        public Task<ProcessResult> TokenAsync()
        {
            return PostAsync("/enterpriseApi/token", new JObject
            {
                {"appId",_apiConfig.AppId },
                {"comCode",_apiConfig.ComCode },
                {"manufacturerUniqueCode",_apiConfig.ManufacturerUniqueCode },
            }, isLogin: true);
        }


        private HttpClient CreateClient()
        {
            var httpClient = CommonUtil.CreateCommonHttpClient();

            //httpClient.DefaultRequestHeaders.TryAddWithoutValidation("openapi-callerId", _apiConfig.CallerId);
            //httpClient.DefaultRequestHeaders.TryAddWithoutValidation("openapi-dataType", "TEXT");
            //httpClient.DefaultRequestHeaders.TryAddWithoutValidation("client_id", _apiConfig.ClientId);
            //httpClient.DefaultRequestHeaders.TryAddWithoutValidation("client_secret", _apiConfig.ClientSecret);
            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 Edg/138.0.0.0");

            return httpClient;
        }

        private async Task<ProcessResult> UploadAsync(string api, string file,
            JObject body)
        {
            var result = new ProcessResult();

            var stringBuilder = new StringBuilder();

            stringBuilder.AppendLine($"开始上传文件：{file}");

            if (!File.Exists(file))
            {
                stringBuilder.AppendLine("文件不存在");
                result.SetError("文件不存在");
                _logger.Error(stringBuilder.ToString());
                return result;
            }

            using var httpClient = CreateClient();

            try
            {
                body ??= new JObject();

                var content = new MultipartFormDataContent();

                body.Add("token", _apiConfig.Authorization);
                stringBuilder.AppendLine($"提交明文参数：{body}");

                var realBody = new JObject
                {
                    { "comCode", _apiConfig.ComCode },
                    { "text", AESEncrypt(body.ToString(Newtonsoft.Json.Formatting.None)) }
                };

                stringBuilder.AppendLine($"提交密文参数：{realBody}");

                var props = realBody.Properties();
                foreach (var prop in props)
                {
                    content.Add(new StringContent(prop.Value.ToString(), Encoding.UTF8), prop.Name);
                }

                Stream s = null;

                var fs = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);

                if (fs.Length > 600 * 1024)
                {
                    s = CommonUtil.CompressImage(fs, size: 600);
                }
                else
                {
                    s = fs;
                }

                content.Add(new StreamContent(s), "file", Path.GetFileName(file));

                var res = await httpClient.PostAsync($"{_apiConfig.Domain}{api}", content);

                s.Dispose();

                var responseStr = await res.Content.ReadAsStringAsync();

                stringBuilder.Append($"接口响应源内容\r\n:{responseStr}");

                if (res.StatusCode != HttpStatusCode.OK)
                {
                    var logContentInner = stringBuilder.ToString();
                    _logger.Error(logContentInner);

                    result.SetError($"接口响应错误:{res.StatusCode}");

                    return result;
                }

                var response = CommonUtil.TryGetJSONObject<APIResponse>(responseStr) ?? new APIResponse
                {
                    code = -1,
                    msg = "解析接口返回失败",
                };

                if (response.msg == "解析接口返回失败")
                {
                    responseStr = AESDecrypt(responseStr);
                    stringBuilder.Append($"解密后接口内容\r\n:{responseStr}");

                    response = CommonUtil.TryGetJSONObject<APIResponse>(responseStr) ?? new APIResponse
                    {
                        code = -1,
                        msg = "解析接口返回失败",
                    };
                }

                var logContent = stringBuilder.ToString();

                if (response.code != 200)
                {
                    //// 如果接口返回提示重复, 说明数据已经同步过了, 最好返回success,告知后续处理,至于后面会不会变, 再看情况定制更改
                    //var responseMsgStr = response.message ?? string.Empty;
                    //if (responseMsgStr.Contains("重复")
                    //    || responseMsgStr.Contains("已存在"))
                    //{
                    //    result.SetSuccess();
                    //    // 记录下日志
                    //    _logger.Info(logContent);
                    //    return result;
                    //}
                    result.SetError(response.msg ?? "解析失败");

                    _logger.Error(logContent);
                }
                else
                {
                    result.SetSuccess(response.data == null ? null
                                : response.data is string ? response.data
                                : JObject.FromObject(response.data)["url"].ToString());

                    _logger.Info(logContent);
                }
            }
            catch (Exception e)
            {
                stringBuilder.AppendLine(e.Message);
                result.SetError(stringBuilder.ToString(), e);
                if (stringBuilder != null)
                {
                    stringBuilder.Append(e.StackTrace);
                    _logger.Error(stringBuilder.ToString());
                }
            }
            finally
            {
                stringBuilder?.Clear();
            }
            return result;

        }

        private string RSADecrypt(string input)
        {
            try
            {
                IAsymmetricBlockCipher engine = new Pkcs1Encoding(new RsaEngine());

                var byteData = Convert.FromBase64String(input);

                //1024长度是128，双方协商好
                int maxBlockSize = 1024 / 8; //解密块最大长度限制

                engine.Init(false, GetPublicKeyParameter());

                int inputLen = byteData.Length;
                using var ms = new MemoryStream();
                int offSet = 0;
                byte[] cache;
                int i = 0;
                // 对数据分段加密
                while (inputLen - offSet > 0)
                {
                    if (inputLen - offSet > maxBlockSize)
                    {
                        cache = engine.ProcessBlock(byteData, offSet, maxBlockSize);
                    }
                    else
                    {
                        cache = engine.ProcessBlock(byteData, offSet, inputLen - offSet);
                    }
                    ms.Write(cache, 0, cache.Length);
                    i++;
                    offSet = i * maxBlockSize;
                }
                return Encoding.UTF8.GetString(ms.ToArray());
            }
            catch (Exception e)
            {
                _logger?.Error($"RSA解密失败：{e.Message}\r\n入参：{input}\r\nPublickKey:{_apiConfig.RSAPublicKey}", e);
            }
            return string.Empty;
        }

        private AsymmetricKeyParameter GetPublicKeyParameter()
        {
            var s = _apiConfig.RSAPublicKey.Replace("\r", "").Replace("\n", "").Replace(" ", "");
            byte[] publicInfoByte = Convert.FromBase64String(s);
            Asn1Object pubKeyObj = Asn1Object.FromByteArray(publicInfoByte);//这里也可以从流中读取，从本地导入   
            return PublicKeyFactory.CreateKey(publicInfoByte);
        }

        private string AESEncrypt(string input)
        {
            try
            {
                using Aes aes = Aes.Create("AesManaged");
                aes.Key = Encoding.UTF8.GetBytes(_apiConfig.AESKey);
                //aes.IV = Keys;
                aes.Mode = CipherMode.ECB;
                aes.Padding = PaddingMode.PKCS7;
                using ICryptoTransform cryptoTransform = aes.CreateEncryptor();
                byte[] bytes = Encoding.UTF8.GetBytes(input);
                return Convert.ToBase64String(cryptoTransform.TransformFinalBlock(bytes, 0, bytes.Length));
            }
            catch (Exception e)
            {
                _logger?.Error($"AES加密失败：{e.Message}\r\n入参：{input}\r\nAESKEY:{_apiConfig.AESKey}", e);
            }
            return string.Empty;
        }

        private string AESDecrypt(string input)
        {
            try
            {
                using Aes aes = Aes.Create("AesManaged");
                aes.Key = Encoding.UTF8.GetBytes(_apiConfig.AESKey);
                //aes.IV = Keys;
                aes.Mode = CipherMode.ECB;
                aes.Padding = PaddingMode.PKCS7;
                using ICryptoTransform cryptoTransform = aes.CreateDecryptor();
                byte[] bytes = Convert.FromBase64String(input);
                return Encoding.UTF8.GetString(cryptoTransform.TransformFinalBlock(bytes, 0, bytes.Length));
            }
            catch (Exception e)
            {
                _logger?.Error($"AES解密失败：{e.Message}\r\n入参：{input}\r\nAESKEY:{_apiConfig.AESKey}", e);
            }

            return string.Empty;
        }

        private async Task<ProcessResult> PostAsync(string api,
            JObject body,
            bool isListResponse = false, bool needUILog = false,
            PassagewayDirection? direction = null,
            ShippingRecord relRecord = null,
            bool isLogin = false)
        {
            var result = new ProcessResult();

            var stringBuilder = new StringBuilder();

            using var httpClient = CreateClient();

            try
            {
                if (!isLogin &&
                    (string.IsNullOrEmpty(_apiConfig.Authorization) || DateTime.Now > _apiConfig.TokenExpireDate))
                {
                    result = await TokenAsync();
                    if (!result.Success)
                    {
                        return result;
                    }

                    _apiConfig.AESKey = RSADecrypt(result.Message);
                    _apiConfig.Authorization = result.Data as string;
                    _apiConfig.TokenExpireDate = DateTime.Now.AddHours(2).AddMinutes(-1);

                    try
                    {
                        lock (_tokenLocker)
                        {
                            var apiKey = Config.Key;
                            // 重新查出来保存登录token
                            using var db = DbService.GetDbContext(_configService);

                            Config = db.SystemConfigDictionaries.FirstOrDefault(p => p.Key == apiKey);

                            Config.StringValue = CommonUtil.AJSerializeObject(_apiConfig);
                            db.Entry(Config).State = EntityState.Modified;

                            db.SaveChanges();
                        }
                    }
                    catch (Exception)
                    {
                    }

                }

                var sign = string.Join(string.Empty, body.Properties()
                    .Where(p => !_unSignFields.Any(q => p.Name.Contains(q))).Select(p => $"{p.Name}{p.Value}"));
                stringBuilder.Append($"待签名字符串:{sign}\r\n");
                using var alg = SHA256.Create();
                sign = Convert.ToHexString(alg.ComputeHash(Encoding.UTF8.GetBytes(sign))).ToLower();

                if (!isLogin)
                {
                    body.Add("token", _apiConfig.Authorization);
                }
                body.Add(nameof(sign), sign);

                if (relRecord != null)
                {
                    stringBuilder.Append($"当前关联车牌号:{relRecord.CarNo}\r\n");
                }

                stringBuilder.Append($"请求明文参数:\r\n{body}\r\n");

                var realBody = isLogin ? body : new JObject();

                if (!isLogin)
                {
                    if (body.ContainsKey("comCode"))
                    {
                        body.Remove("comCode");
                    }

                    realBody.Add("comCode", _apiConfig.ComCode);
                    realBody.Add("text", AESEncrypt(body.ToString(Newtonsoft.Json.Formatting.None)));
                }

                var realBodyStr = realBody.ToString(Newtonsoft.Json.Formatting.None);

                stringBuilder.Append($"实际请求参数:\r\n{realBodyStr}\r\n");
                var content = new StringContent(realBodyStr, Encoding.UTF8, "application/json");

                var res = await httpClient.PostAsync($"{_apiConfig.Domain}{api}", content);

                var responseStr = await res.Content.ReadAsStringAsync();

                stringBuilder.Append($"接口响应源内容\r\n:{responseStr}");

                if (res.StatusCode != HttpStatusCode.OK)
                {
                    var logContentInner = stringBuilder.ToString();
                    _logger.Error(logContentInner);

                    if (needUILog)
                    {
                        OnAPILogsRaised(direction, logContentInner);
                    }

                    result.SetError($"接口响应错误:{res.StatusCode}");

                    return result;
                }

                var response = CommonUtil.TryGetJSONObject<APIResponse>(responseStr) ?? new APIResponse
                {
                    code = -1,
                    msg = "解析接口返回失败",
                };

                if (response.msg == "解析接口返回失败")
                {
                    responseStr = AESDecrypt(responseStr);
                    stringBuilder.Append($"解密后接口内容\r\n:{responseStr}");

                    response = CommonUtil.TryGetJSONObject<APIResponse>(responseStr) ?? new APIResponse
                    {
                        code = -1,
                        msg = "解析接口返回失败",
                    };
                }

                var logContent = stringBuilder.ToString();

                if (response.code != 200)
                {
                    //// 如果接口返回提示重复, 说明数据已经同步过了, 最好返回success,告知后续处理,至于后面会不会变, 再看情况定制更改
                    //var responseMsgStr = response.message ?? string.Empty;
                    //if (responseMsgStr.Contains("重复")
                    //    || responseMsgStr.Contains("已存在"))
                    //{
                    //    result.SetSuccess();
                    //    // 记录下日志
                    //    _logger.Info(logContent);
                    //    return result;
                    //}
                    result.SetError(response.msg ?? "解析失败");

                    _logger.Error(logContent);
                    if (needUILog)
                    {
                        OnAPILogsRaised(direction, logContent);
                    }
                }
                else
                {
                    if (isLogin)
                    {
                        var resObj = response.data as JObject;
                        result.SetSuccess(resObj["token"].ToString(), resObj["aceSecret"].ToString());
                    }
                    else
                    {
                        var innerRes = response.GetInnerResponse();

                        if (innerRes.status)
                        {
                            result.SetSuccess(innerRes);
                        }
                        else
                        {
                            result.SetError(innerRes.msg);
                        }

                        //result.SetSuccess(isListResponse
                        //    ? (response.data == null ? null : JArray.FromObject(response.data))
                        //    : (response.data == null ? null
                        //        : response.data is string ? response.data : JObject.FromObject(response.data)));
                    }

                    if (needUILog)
                    {
                        OnAPILogsRaised(direction, logContent);
                    }

                    _logger.Info(logContent);
                }

            }
            catch (Exception e)
            {
                stringBuilder.AppendLine(e.Message);
                result.SetError(stringBuilder.ToString(), e);
                if (stringBuilder != null)
                {
                    stringBuilder.Append(e.StackTrace);
                    _logger.Error(stringBuilder.ToString());
                }
                if (needUILog)
                {
                    OnAPILogsRaised(direction, stringBuilder.ToString());
                }
            }
            finally
            {
                stringBuilder?.Clear();
            }
            return result;
        }

        public class APIConfig : APIConfigBase
        {
            [DynamicFormItemDefine("接口域名")]
            public string Domain { get; set; }

            [DynamicFormItemDefine("企业APPID")]
            public string AppId { get; set; }

            [DynamicFormItemDefine("企业编码")]
            public string ComCode { get; set; }

            [DynamicFormItemDefine("厂商标识码")]
            public string ManufacturerUniqueCode { get; set; }

            [DynamicFormItemDefine("监管平台RSA公钥", VbenAdminFormType.InputTextArea)]
            public string RSAPublicKey { get; set; }

            [DynamicFormItemDefine("AES密钥", Disable = true)]
            public string AESKey { get; set; }

            [DynamicFormItemDefine("登录认证Token", Disable = true)]
            public string Authorization { get; set; }

            [DynamicFormItemDefine("登录认证Token过期时间", Disable = true)]
            public DateTime TokenExpireDate { get; set; }

            /// <summary>
            /// 专网地址
            /// </summary>
            [DynamicFormItemDefine("专网地址(可带端口号)")]
            public string PrivateNetworkAddress { get; set; }

            /// <summary>
            /// 图片/视频目录
            /// </summary>
            [DynamicFormItemDefine("图片/视频目录")]
            public string FileFolder { get; set; }

            /// <summary>
            /// 录制过车视频时长(秒)
            /// </summary>
            [DynamicFormItemDefine("过车视频时长(秒)")]
            public int VideoDuration { get; set; }

            /// <summary>
            /// 启用视频录制
            /// </summary>
            [DynamicFormItemDefine("启用视频录制", VbenAdminFormType.Switch)]
            public bool EnableVideoCapture { get; set; }

        }

        public class APIResponse
        {
            public int code { get; set; }
            public string msg { get; set; }
            public object data { get; set; }

            public APIResponseInner GetInnerResponse()
            {
                return CommonUtil.TryGetJSONObject<APIResponseInner>(data.ToString()) ?? new APIResponseInner
                {
                    status = false,
                    msg = "解析失败"
                };
            }
        }

        public class APIResponseInner
        {
            public bool status { get; set; }

            public string msg { get; set; }

            public string result { get; set; }

            public string reason { get; set; }
        }
    }
}
